package com.Memory;

import com.ProcessManagement.PCB;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class VirtualMemory {
    private static List<PCB> pcbList;
    private static List<List<Integer>> sequences;
    private static int listCount[] = new int[4];
    private static int LFUCount[] = new int[4];
    private static int LFUCountNum = 0;
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        boolean isRunning = true;

        while (isRunning) {
            displayVirtualMemoryMenu();
            sequences = readAccessingVariables("src/input/AccessingVariables.txt");
            int choice = scanner.nextInt();

            switch (choice) {
                case 1:
                    FIFO();
                    break;
                case 2:
                    LRU();
                    break;
                case 3:
                    LFU();
                    break;
                case 4:
                    isRunning = false;
                    break;
                default:
                    System.out.println("无效的选择，请重新输入。");
            }
        }

        scanner.close();
    }

//    打印选择算法的菜单
    private static void displayVirtualMemoryMenu() {
        System.out.println("------ 主菜单 ------");
        System.out.println("虚拟存储器模块管理：");
        System.out.println("1. 先进先出调度算法（FIFO）");
        System.out.println("2. 最近最少使用调度算法（LRU）");
        System.out.println("3. 最近最不常用调度算法（LFU）");
        System.out.println("4. 退出主菜单");
    }

//    读取AccessingVariables文件
    public static List<List<Integer>> readAccessingVariables (String fileName) {
        List<List<Integer>> sequences = new ArrayList<>();

        try {
            BufferedReader reader = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = reader.readLine()) != null) {
                List<Integer> sequence = parseLine(line);
                sequences.add(sequence);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return sequences;
    }

//    读取AccessingVariables文件后数据处理
    private static List<Integer> parseLine(String line) {
        String[] tokens = line.split(",");
        List<Integer> sequence = new ArrayList<>();
        for (String token : tokens) {
            sequence.add(Integer.parseInt(token));
        }
        return sequence;
    }

//    从List<List<Integer>>提起一行数据
    public static int[] getRowValues(List<List<Integer>> sequences, int rowIndex) {
        List<Integer> row = sequences.get(rowIndex);
        int[] rowValues = new int[row.size()];

        for (int i = 0; i < row.size(); i++) {
            rowValues[i] = row.get(i);
        }

        return rowValues;
    }

//    先进先出调度算法
    public static void FIFO() {
        //        读取pcb_list.txt文件并获取PCB列表。
        pcbList = readPCBFromFile("src/input/pcb_list.txt");
        //        Memory[100] 有124个物理块#0表示没有分配 1表示分配 初始化时0-39为1,其他为0
        int[] Memory = new int[124];
        for (int i = 0; i < 124; i++) {
            Memory[i]=0;
        }
        for (int i = 0; i < 40; i++) {
            Memory[i]=1;
        }
        for (PCB pcb : pcbList) {
            for (int i = 0; i < listCount.length; i++) {
                listCount[i] = 0;
            }
            //        Pagelist #页号，内存块号
            int[] Pagelist;
            Pagelist = request(pcb.getInstructNum()*10,40);
//        从Memory中找到4个空白页，分配给进程，更新pagelist
            for (int j = 0; j <4; j++) {
                for (int i = 0; i < 100; i++) {
                    if (Memory[i]==0){
                        Pagelist[j]=i;
                        Memory[i]=1;
                        break;
                    }
                }
            }
//        Stack[4]  #记录访问
            int[] Stack = new int[4];
//        初始化为-1
            for (int i = 0; i < 4; i++) {
                Stack[i]=-1;
            }

            System.out.println("-----------------");
            System.out.println("进程ID："+pcb.getProID());

//        定义访问变量，调用read（），并记录缺页次数，返回缺页次数
            int[] array= getRowValues(sequences, pcbList.indexOf(pcb));
//        缺页数
            int count=0;
            for (int i = 0; i < array.length; i++) {
                System.out.println("访问页表："+array[i]);
                if (!readFIFO(array[i],Stack,Pagelist,Memory)){
                    count++;
                }
                System.out.println("Stack记录：");
                for (int j = 0; j < Stack.length; j++) {
                    System.out.print(Stack[j]+",");
                }
                System.out.println("");
                //        页表Pagelist的占用情况
                System.out.println("页表Pagelist分配的物理块号：");
                for (int j = 0; j < Pagelist.length; j++) {
                    System.out.print(Pagelist[j]+",");
                }
                System.out.println("");
            }
            System.out.println("缺页数为："+count);
//            回收内存
            for (int i = 40; i < Memory.length; i++) {
                Memory[i]=0;
            }
        }
    }

//    最近最少使用调度算法（LRU）
    public static void LRU() {
//        读取pcb_list.txt文件并获取PCB列表。
        pcbList = readPCBFromFile("src/input/pcb_list.txt");
        //        Memory[100] 有124个物理块#0表示没有分配 1表示分配 初始化时0-39为1,其他为0
        int[] Memory = new int[124];
        for (int i = 0; i < 124; i++) {
            Memory[i]=0;
        }
        for (int i = 0; i < 40; i++) {
            Memory[i]=1;
        }
        for (PCB pcb : pcbList) {
            //        Pagelist #页号，内存块号
            int[] Pagelist;
            Pagelist = request(pcb.getInstructNum()*10,40);
//        从Memory中找到4个空白页，分配给进程，更新pagelist
            for (int j = 0; j <4; j++) {
                for (int i = 0; i < 100; i++) {
                    if (Memory[i]==0){
                        Pagelist[j]=i;
                        Memory[i]=1;
                        break;
                    }
                }
            }
//        Stack[4]  #记录访问
            int[] Stack = new int[4];
//        初始化为-1
            for (int i = 0; i < 4; i++) {
                Stack[i]=-1;
            }

            System.out.println("-----------------");
            System.out.println("进程ID："+pcb.getProID());

//        定义访问变量，调用read（），并记录缺页次数，返回缺页次数
            int[] array= getRowValues(sequences, pcbList.indexOf(pcb));
//        缺页数
            int count=0;
            for (int i = 0; i < array.length; i++) {
                System.out.println("访问页表："+array[i]);
                if (!read(array[i],Stack,Pagelist,Memory)){
                    count++;
                }
                System.out.println("Stack记录：");
                for (int j = 0; j < Stack.length; j++) {
                    System.out.print(Stack[j]+",");
                }
                System.out.println("");
                //        页表Pagelist的占用情况
                System.out.println("页表Pagelist分配的物理块号：");
                for (int j = 0; j < Pagelist.length; j++) {
                    System.out.print(Pagelist[j]+",");
                }
                System.out.println("");
            }
            System.out.println("缺页数为："+count);
//            回收内存
            for (int i = 40; i < Memory.length; i++) {
                Memory[i]=0;
            }
        }

    }

//    最近最不常用调度算法（LFU）,每访问8次计算一次
    private static void LFU() {
        //        读取pcb_list.txt文件并获取PCB列表。
        pcbList = readPCBFromFile("src/input/pcb_list.txt");
        //        Memory[100] 有124个物理块#0表示没有分配 1表示分配 初始化时0-39为1,其他为0
        int[] Memory = new int[124];
        for (int i = 0; i < 124; i++) {
            Memory[i]=0;
        }
        for (int i = 0; i < 40; i++) {
            Memory[i]=1;
        }
        for (PCB pcb : pcbList) {
            for (int i = 0; i < LFUCount.length; i++) {
                LFUCount[i] = 0;
            }
            LFUCountNum = 0;
            //        Pagelist #页号，内存块号
            int[] Pagelist;
            Pagelist = request(pcb.getInstructNum()*10,40);
//        从Memory中找到4个空白页，分配给进程，更新pagelist
            for (int j = 0; j <4; j++) {
                for (int i = 0; i < 100; i++) {
                    if (Memory[i]==0){
                        Pagelist[j]=i;
                        Memory[i]=1;
                        break;
                    }
                }
            }
//        Stack[4]  #记录访问
            int[] Stack = new int[4];
//        初始化为-1
            for (int i = 0; i < 4; i++) {
                Stack[i]=-1;
            }

            System.out.println("-----------------");
            System.out.println("进程ID："+pcb.getProID());

//        定义访问变量，调用read（），并记录缺页次数，返回缺页次数
            int[] array= getRowValues(sequences, pcbList.indexOf(pcb));
//        缺页数
            int count=0;
            for (int i = 0; i < array.length; i++) {
                System.out.println("访问页表："+array[i]);
                if (!readLFU(array[i],Stack,Pagelist,Memory)){
                    count++;
                }
                System.out.println("Stack记录：");
                for (int j = 0; j < Stack.length; j++) {
                    System.out.print(Stack[j]+",");
                }
                System.out.println("");
                //        页表Pagelist的占用情况
                System.out.println("页表Pagelist分配的物理块号：");
                for (int j = 0; j < Pagelist.length; j++) {
                    System.out.print(Pagelist[j]+",");
                }
                System.out.println("");
            }
            System.out.println("缺页数为："+count);
//            回收内存
            for (int i = 40; i < Memory.length; i++) {
                Memory[i]=0;
            }
        }
    }

//    读pcb
    public static List<PCB> readPCBFromFile(String fileName) {
        List<PCB> pcbList = new ArrayList<>();

        File file = new File(fileName);
        try {
            Scanner scanner = new Scanner(file);
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                String[] parts = line.split(" ");

                if (parts.length == 4) {
                    int proID = Integer.parseInt(parts[0]);
                    int startTimes = Integer.parseInt(parts[1]);
                    int instructNum = Integer.parseInt(parts[2]);
                    int Priority = Integer.parseInt(parts[3]);
                    // 创建PCB对象并添加到列表中
                    PCB pcb = new PCB(proID, startTimes, instructNum, Priority);
                    pcb.setPSW("null");  // 设置进程状态为null
                    pcb.setEndTimes(0);  // 设置结束时间
                    pcbList.add(pcb);
                }
                else if(parts.length == 3){
                    int proID = Integer.parseInt(parts[0]);
                    int startTimes = Integer.parseInt(parts[1]);
                    int instructNum = Integer.parseInt(parts[2]);
                    int Priority = 0;
                    // 创建PCB对象并添加到列表中
                    PCB pcb = new PCB(proID, startTimes, instructNum, Priority);
                    pcb.setPSW("null");  // 设置进程状态为null
                    pcb.setEndTimes(0);  // 设置结束时间
                    pcbList.add(pcb);
                }
                else System.out.println("文件job_list.txt格式错误!!!");
            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return pcbList;
    }

//        Pagelist长度为⌈total/10⌉，返回pagelist
    public static int[] request(int total,int getMax){
        int count = (int)total/10;
        int[] Pagelist=new int[count];
//        初始化为-1
        for (int i = 0; i < Pagelist.length; i++) {
            Pagelist[i]=-1;
        }
        return Pagelist;
    }

//        实现访问页面的模拟操作，返回false，表示发生缺页，调用后返回页面，返回true，表示未发生缺页，直接返回。
    public static boolean read(int page_number,int[] Stack,int[] Pagelist,int[] Memory){
        for (int i = 0; i < Stack.length; i++) {
            if (page_number==Stack[i]){
                for (int j =i; j >0; j--) {
                    Stack[j]=Stack[j-1];
                }
                Stack[0]=page_number;
                return true;
            }
        }
//        设置Pagelist
        if (Pagelist[page_number]==-1){
            for (int i = 0; i < Memory.length; i++) {
                if (Memory[i]==0){
                    Pagelist[page_number]=i;
                    Memory[i]=1;
                    break;
                }
            }
        }
        if (Stack[Stack.length-1]!=-1) Pagelist[Stack[Stack.length-1]]=-1;
        for (int i = Stack.length-1; i >0; i--) {
            Stack[i]=Stack[i-1];
        }
        Stack[0]=page_number;
        return false;
    }

    public static boolean readFIFO(int page_number,int[] Stack,int[] Pagelist,int[] Memory){
        for (int i = 0; i < Stack.length; i++) {
            if (page_number==Stack[i]) return true;
        }
//        设置Pagelist
        if (Pagelist[page_number]==-1){
            for (int i = 0; i < Memory.length; i++) {
                if (Memory[i]==0){
                    Pagelist[page_number]=i;
                    Memory[i]=1;
                    break;
                }
            }
        }
//        设置stack
        for (int i = 0; i < Stack.length; i++) {
            if (Stack[i] == -1){
                Stack[i] = page_number;
                listCount[i]++;
                for (int j = 0; j < listCount.length; j++) {
                    if (listCount[j]!=0 && j!=i) listCount[j]++;
                }
                return false;
            }
        }
        int maxVal = 0;
        int maxindex = 0;
        for (int j = 0; j < listCount.length; j++) {
            if (listCount[j] > maxVal) {
                maxVal = listCount[j];
                maxindex = j;
            }
        }
//        先回收内存
        Pagelist[Stack[maxindex]] = -1;
        Stack[maxindex] = page_number;
        listCount[maxindex] = 0;
        return false;
    }

    public static boolean readLFU(int page_number,int[] Stack,int[] Pagelist,int[] Memory){
        for (int i = 0; i < Stack.length; i++) {
            if (page_number==Stack[i]) {
                LFUCount[i]++;
                LFUCountNum++;
                return true;
            }
        }
//        设置Pagelist
        if (Pagelist[page_number]==-1){
            for (int i = 0; i < Memory.length; i++) {
                if (Memory[i]==0){
                    Pagelist[page_number]=i;
                    Memory[i]=1;
                    break;
                }
            }
        }
//        设置stack
        for (int i = 0; i < Stack.length; i++) {
            if (Stack[i] == -1){
                Stack[i] = page_number;
                LFUCount[i]++;
                LFUCountNum++;
                return false;
            }
        }
        int minVal = 1000;
        int minindex = 0;
        for (int j = 0; j < LFUCount.length; j++) {
            if (LFUCount[j] < minVal) {
                minVal = LFUCount[j];
                minindex = j;
            }
        }
//        先回收内存
        Pagelist[Stack[minindex]] = -1;
        Stack[minindex] = page_number;
        LFUCount[minindex]++;
        LFUCountNum++;
//        LFUCount[minindex] = 0;
//        每隔8次清空计时
        if (LFUCountNum>=8){
            for (int i = 0; i < LFUCount.length; i++) {
                LFUCount[i] = 0;
            }
        }
        return false;
    }
}

